import math
import time

from FPDLCore import Configs
from FPDLCore.WorkerManager import WorkerManager
from FPDLCore.EntryClient import EntryClient

NOT_IN_DOWNLOADING = 0
IN_DOWNLOADING = 1
DOWNLOADED = 2

class FileDownloadClient:
    def __init__(self, sel, remotefilepath, localsavepath):
        self.remotefilepath = remotefilepath
        self.localsavepath = localsavepath
        self.sel = sel
        self.workmanager = WorkerManager(sel)
        self.datachunkinfo = []
        self.totaldownloaded = 0

    def startdownload(self):
        def onworker(worker):
            if worker:
                chunkinfo = self.getnotdownloadingchunk()
                if not chunkinfo is None:
                    self.datachunkinfo[chunkinfo[0]] = IN_DOWNLOADING
                    def ondownloadend(data):
                        self.ondownloaded(chunkinfo[0], data)
                        onworker(worker)
                    worker.startdownload(chunkinfo[1],
                                         chunkinfo[2],
                                         self.remotefilepath,
                                         ondownloadend)
            else:
                self.workmanager.dowork((Configs.SERVER_ADDRESS, self.entryserverrespond.port), onworker)

        def onentry(respond):
            self.entryserverrespond = respond
            if respond.filesize == 0:
                print(respond.errormsg)
                return
            self.starttime = time.time()
            self.initdatachunkinfo(respond.filesize)
            for i in range(Configs.CONNECTIONS_PER_DOWNLOAD):
                self.workmanager.dowork((Configs.SERVER_ADDRESS, respond.port), onworker)
        entryclient = EntryClient((Configs.SERVER_ADDRESS, Configs.SERVER_PORT), self.sel, self.remotefilepath, onentry)
        entryclient.connect()


    def initdatachunkinfo(self, filesize):
        chunkcount = math.ceil(filesize/Configs.DATA_CHUNK_SIZE)
        self.datachunkinfo = []
        for i in range(chunkcount):
            self.datachunkinfo.append(NOT_IN_DOWNLOADING)
        self.savefile = open(self.localsavepath, 'wb')

    def getnotdownloadingchunk(self):
        for i in range(len(self.datachunkinfo)):
            if self.datachunkinfo[i] == NOT_IN_DOWNLOADING:
                begpos = i * Configs.DATA_CHUNK_SIZE
                endpos = (i+1) * Configs.DATA_CHUNK_SIZE
                if endpos > self.entryserverrespond.filesize:
                    endpos = self.entryserverrespond.filesize
                return i, begpos, endpos

    def ondownloaded(self, chunkindex, data):
        self.savefile.seek(chunkindex*Configs.DATA_CHUNK_SIZE)
        self.savefile.write(data)
        self.datachunkinfo[chunkindex] = DOWNLOADED
        completed = True
        left = 0
        for i in range(len(self.datachunkinfo)):
            if self.datachunkinfo[i] != DOWNLOADED:
                completed = False
                left += 1
        self.totaldownloaded += len(data)
        speed = self.totaldownloaded/(time.time() - self.starttime)/1024
        print(str.format(">>>>chunk:{} download completed, left:{}, everage speed:{}KB/s", chunkindex, left, speed))
        if completed:
            print("file download completed")
            self.savefile.close()
            self.workmanager.shutdown()